import re

from typing import Union

import settings
from common.core.meta import Xpath
from common.utils.parser import load_yaml


# 为元素查找提供支持
class ElementsLookUpMixin:
    elements = {}

    def __init__(self):
        match self.elements:
            case str() as path:
                self.elements = load_yaml(settings.join_element_path(path))
            case tuple() as paths:
                self.elements = load_yaml(settings.join_element_path(*paths))
            case dict():
                ...
            case _:
                raise TypeError(f"{self.__name__}中 elements 必须是合法的yaml文件路径或者一个元素字典")

    def _get_locator_by_name(self, element_name: str) -> Union[Xpath, str, dict]:
        try:
            return self.elements[element_name]
        except KeyError:
            raise LookupError(f"不存在的 xpath key {element_name}") from None

    def get_locator(self, element: Union[Xpath, str]) -> Xpath:
        match element:
            case str() as name:
                locator = self._get_locator_by_name(name)
                match locator:
                    # locator 是 xpath 表达式
                    case str() as xpath_exp:
                        return Xpath(xpath_exp, name=name)
                    # 字典类型（来自yaml文件）
                    case dict() as locator_info:
                        return Xpath.from_dict(name, locator_info)
            case Xpath():
                return element
            case _:
                raise TypeError(f"定位器只 {element} 接受一个 {Xpath.__name__}对象或字符串")

    def set_locator(self, element_name: str, locator: Union[Xpath, str]) -> None:
        match locator:
            case str() | Xpath():
                self.elements.update(element_name, locator)
            case _:
                raise TypeError(f"定位器 {element_name} 的数据类型只能是 str 或者 Xpath")

    # xpath 格式化复用
    def fmt_locator(self, element_name: Union[str, Xpath], *args) -> Xpath:
        locator = self.get_locator(element_name)
        if re.match(r"{(.*?)}", locator.xpath_exp):
            raise ValueError(f"定位器 {locator.name} 的xpath表述中不允让使用{{}}")
        if (le := locator.xpath_exp.count("%s")) != len(args):
            raise ValueError(
                f"定位器 {locator.name} 的xpath表达式 {locator.xpath_exp} 需要 {le} 中参数，当前格式化参数 {args}")
        locator.xpath_exp = locator.xpath_exp % args
        return locator
